require 'paperclip/upfile'
require 'RMagick'
include Magick

####################################
#                                  #
#  classes for working with files  #
#                                  #
####################################


# the base class
# please override the execute method
# 
# todo: make files more than one
# 

class FileAction 
  attr_accessor :req
 
  def initialize(request)
    @req = request
  end
 
  #returns a request and a message
  def execute
    RAILS_DEFAULT_LOGGER.error 'execute for ' + @filename + ' not implemented'
  end
  
  protected
    def change_ext(ext)
      File.basename(@req.get_full_link, File.extname(@req.get_full_link)) + ext
    end
    
    def addfile(filename)       
      req = Request.new(:from_id => @req.id, :session_id => @req.session_id)
      req.file = File.new(filename)      
      req.save
      req.save #calling update here, to set a valid content type 
      req
    end
    
    def replace_file(filename)    
      newname = File.dirname(@req.get_full_link) + '/' + change_ext(File.extname(filename))
      File.rename(filename, newname)
      RAILS_DEFAULT_LOGGER.debug "replace with file: " + newname
      @req.file = File.new(newname)
      @req.save      
      @req
    end;
end

#simply copy the file 
class CopyFile < FileAction
  def execute
     RAILS_DEFAULT_LOGGER.debug "uploading file: " + @req.get_full_link
     return addfile(@req.get_full_link),'a copy of the file was created'
  end
end

# action that executes a system command
#
# 1. create a temp dir
# 2. execute the shell command
# 3. clear the temp dir
#
######################################
class SystemAction < FileAction
    def initialize(request)
      super(request)
      #create a temporary directory
      @dirname = '/tmp/ff'+rand(10000000).to_s
      Dir.mkdir(@dirname)
      @files_count = 0
    end

  protected     
    def sys_cmd(command, params)
      #execute the shell command
      
      lcommand = command + " '" + params.join("' '") + "'"
      RAILS_DEFAULT_LOGGER.info lcommand
     
      #escape params 
      if not system(lcommand)
        raise('os error: ' + $?.to_s)
      end
    end

    def delete_dir(dir)
      sys_cmd('rm -rf',[@dirname])
    end

    def process_files(replace)      
      files = []
      Dir.foreach(@dirname) do |x| 
        if File.stat(get_full_name(x)).file? 
          files[0,0] = x 
        end  
      end        

      @files_count = files.count
      
      if replace and (files.count == 1)
        result = replace_file(get_full_name(files[0]))
      else
        for x in files 
          result = addfile(get_full_name(x))
        end  
        if files.count > 1
          result = @req
        end
      end
      
      delete_dir(@dirname)
      result
    end

    def execute_system(command, params, replace)      
      sys_cmd(command, params)
      process_files(replace)
    end        
    
    private
      def get_full_name(filename)
        @dirname + '/' + filename
      end

    protected
      def pretty_files_count
        @files_count == 0 ? 'no' : @files_count.to_s + ' ' + (@files_count ==1 ? 'file' : 'files') 
      end
end

class AbstractArchive < SystemAction
  public
    #extract all files from archive int a tempdir  
    def extract_all(dir)
      raise self.class.to_s + " extract_all not implemented"    
    end

  protected  
    #get all files in @dirname and add them to archive
    def create_archive
      raise self.class.to_s + " create_archive not implemented"
    end
  public
    def execute_archive(aarchive)
      #extract the old archive
      aarchive.extract_all(@dirname)
      old_ext = @req.ext.upcase
      #put all files into archive      
      filename = create_archive
      new_ext = File.extname(filename).delete('.').upcase
      delete_dir(@dirname)
      #replace the new archive
      return replace_file(filename), "archive converted from #{old_ext} to #{new_ext} "
    end      
end

class ZipArchive < AbstractArchive
  def execute
    return execute_system('zip',['-D',@dirname+'/'+change_ext('.zip'), @req.get_full_link], true), 'a ZIP archive was created'
    #-D option makes zip skip directories
  end

  def extract_all(dir)
    sys_cmd('unzip',[@req.get_full_link,'-d',dir])
  end

  protected
    def create_archive
      filename = @dirname + ".zip"
      sys_cmd("cd #{@dirname} ; zip", ['-r',filename,'.'])
      filename
    end
end

class UnzipArchive < SystemAction
  def execute
    return execute_system('unzip',['-o','-j','-d',@dirname,@req.get_full_link], true), pretty_files_count + ' unzipped from archive'
  end
end

class RarArchive < AbstractArchive
  def execute
    return execute_system('rar',['a','-ep',@dirname+'/'+change_ext('.rar'),@req.get_full_link], true), 'a RAR archive was created'
    #-ep switch makes rar skip directories
  end
    def extract_all(dir)
      sys_cmd('rar',['e',@req.get_full_link,dir])
    end

  protected  
    def create_archive
      filename = @dirname + '.rar'
      sys_cmd("cd #{@dirname} ; rar", ['a','-r',filename,'.'])
      filename
    end
end

class UnrarArchive < SystemAction
  def execute
    return execute_system('rar',['e','-ep',@req.get_full_link,@dirname],true),  pretty_files_count + ' extracted from archive'
  end
end

#convert images with imagemagick
class ConvertImage < FileAction  
  def initialize(request, to_format)
    super(request)
    @to_format = to_format
  end
  
  def execute
    RAILS_DEFAULT_LOGGER.debug "converting image to " + @to_format  
    filename = @req.get_full_link
    img = Magick::Image.read(filename).first
    newfile = '/tmp/' + change_ext('.'+@to_format)
    img.write newfile
    return replace_file(newfile), "image converted from #{File.extname(filename).delete(".").upcase} to #{@to_format.upcase}"
  end  
  
  def self.create_thumbnail(aimage, athumb)
    if not system("convert '#{aimage}' -thumbnail 300x300 '#{athumb}'")
      raise ($?.to_s)
    end
  end  
end

class OOConvert < SystemAction
  def initialize(request, filter, extension)
    super(request)
    @filter = filter
    @extension = extension
  end  

  def execute
    old_ext = File.extname(@req.file_file_name).delete('.').upcase
    rez = execute_system("#{RAILS_ROOT}/oo/ooconvert.py",['-f',@req.get_full_link,'-a',@filter,'-t',@dirname + '/' + change_ext('.'+@extension)],true)
    ok_str = "document converted from #{old_ext} to #{@extension.upcase}"
    return rez, ok_str
  end      
end
